package com.xiao.config;

import com.xiao.filter.JwtAuthenticationTokenFilter;
import com.xiao.handler.security.AccessDeniedHandlerImpl;
import com.xiao.handler.security.AuthenticationEntryPointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired// 过滤器
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;


    // --------- 认证/授权 失败处理器 Start ---------
    @Autowired// 认证失败处理器
    AuthenticationEntryPointImpl authenticationEntryPoint;
    @Autowired// 授权失败处理器
    AccessDeniedHandlerImpl accessDeniedHandler;
    // --------- 认证/授权 失败处理器 End ---------

    // 发现你实现了UserDetailsService接口就不会自动构建一个默认的AuthenticationManager。这时候开发者就必须自定义AuthenticationManagerBuilder或者主动暴露一个authenticationManagerBean，否则AuthenticationManager就会是null
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean// Spring Security 提供的一个密码编码器，用于存储用户密码
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception { // 定义HTTP安全配置的方法
        http
                // 关闭 csrf [相当于开启防护]
                .csrf().disable() // CSRF（Cross-Site Request Forgery，跨站请求伪造）是一种网络攻击方式，攻击者利用受害者的身份在受害者已登录的网站上执行非本意的操作。这种攻击通常发生在受害者点击了攻击者控制的网页、链接或按钮，或者是访问了包含恶意代码的页面时。
                // 不通过 Session获取 SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登入请求，允许匿名访问
                .antMatchers("/login").anonymous()
                .antMatchers("/logout").authenticated()// 注销请求 是必须要要通过认证才能访问。(因为认证没通过，不需要注销)
                .antMatchers("/comment").authenticated()// 评论请求 是必须要要通过认证才能访问
                //.antMatchers("/upload").authenticated()// 因为他请求没有携带 token 应该是前端，没提供 token 因此，我们不能通过  (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal() 拿到 用户
                .antMatchers("/user/userInfo").authenticated()// 查询个人信息 必须要要通过认证才能访问。(因为没有认证不会有个人信息)
                // 添加测试 Jwt 过滤器测试用，如果测试没有问题 下面这个可以删除
                //.antMatchers("/link/**").authenticated() // 需要认证才能访问
                // 除了 以上请求,其他请求都不需要认证即可访问
                .anyRequest().permitAll();

        // 关闭默认的注销功能
        http.logout().disable();

        // 把我们创建的过滤器，添加到 springSecurity 中
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        // ------ 异常处理器 Start ------
        http.exceptionHandling()

                .authenticationEntryPoint(authenticationEntryPoint) // 把我们创建的 AuthenticationEntryPoint 认证失败处理器，添加到 springSecurity 中
                .accessDeniedHandler(accessDeniedHandler); // 把我们创建的 accessDeniedHandler 授权失败处理器，添加到 springSecurity 中
        // ------ 异常处理器 End ------
        // 允许跨域
        http.cors();


// 之前写的，防止跳转到 Security 登入页面。
/*        // ------ 取消 Security 的 login 页面 --- Start ----
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)// 会话管理策略为无状态; Spring Security不会为每个请求创建一个单独的会话，而是使用一个共享的会话
                .and()
                .authorizeRequests()// 配置请求的授权规则
                .antMatchers("/login").anonymous() // 表示对于路径 /login 的请求，允许匿名访问，即不需要用户登录即可访问
                .anyRequest().permitAll(); // any=anyway 所有; 对于任何其他请求，都允许所有用户访问，即不需要认证。
        // ------ 取消 Security 的 login 页面 --- End ----*/
    }



}